package Bio::Rambam::Proteomics::Structures::Spectrum;

use Moose;
use namespace::autoclean;
use Data::Dumper;
use Moose::Util::TypeConstraints;
use MIME::Base64;
use Compress::Zlib ;
use Bio::Rambam::Proteomics qw($FRAGMENTATIONS);

##### <spectrum>
has 'index'					=>	(is=>'rw',isa=>'Int');
has 'id'					=>	(is=>'rw',isa=>'Str');	
has 'defaultArrayLength'	=>	(is=>'rw',isa=>'Int');	#Default length of binary data arrays contained in this element (element "spectrum"): i.e. number of peaks.
has 'ms_level'				=>	(is=>'rw',isa=>'Int');	#MS1, MS2, MS3, ...MS:1000511

enum 'scan_combination_types',[qw('mean of spectra' 'median of spectra' 'sum of spectra' 'no combination')];	# children to "spectra combination" (MS:1000570): mean of spectra(MS:1000575), median of spectra(MS:1000573), sum of spectra(MS:1000571), no combination(MS:1000795)
# scan/s information
has 'scan_count'			=>	(is=>'rw',isa=>'Int');	# Number of scans associated with the spectrum
has 'scan_combination_type'	=>	(is=>'rw',isa=>'scan_combination_types');# see enum scan_combination_types

#	Binary spectrum array data
enum 'endianess', [qw(little big)];
enum 'bit_precission', [qw(32 64)];
enum 'compression', [qw( no_compression zlib_compression)];
enum 'fragmentation_type', [qw(LIFT BIRD CID ECD ETD HCD ISCID IRMPD LCD MPD PD PSD PQD SID SORI)];
has 	'mz_ArrayBinary_precission'			=>	(is=>'rw',	isa=>'bit_precission',	clearer => 'clear_mz_ArrayBinary_precission');
has 	'mz_ArrayBinary_endian'				=>	(is=>'rw',	isa=>'endianess', 		clearer => 'clear_mz_ArrayBinary_endian', default=>'little'); #all mzML supposed to be little
has 	'mz_ArrayBinary_array'				=>	(is=>'rw',	isa=>'Str', 			clearer => 'clear_mz_ArrayBinary_array');
has		'mz_ArrayBinary_compression'		=>	(is=>'rw',	isa=>'compression', 	clearer => 'clear_mz_ArrayBinary_compression');
has 	'int_ArrayBinary_precission'		=>	(is=>'rw',	isa=>'bit_precission', 	clearer => 'clear_int_ArrayBinary_precission');
has 	'int_ArrayBinary_endian'			=>	(is=>'rw',	isa=>'endianess', 		clearer => 'clear_int_ArrayBinary_endian', default=>'little'); #all mzML supposed to be little
has 	'int_ArrayBinary_array'				=>	(is=>'rw',	isa=>'Str', 			clearer => 'clear_int_ArrayBinary_array');
has		'int_ArrayBinary_compression'		=>	(is=>'rw',	isa=>'compression', 	clearer => 'clear_int_ArrayBinary_compression');
has 	'mz_encodedLength'					=>	(is=>'rw',	isa=>'Int');
has 	'int_encodedLength'					=>	(is=>'rw',	isa=>'Int');

#	Spectral data (non-binary)
has 	'array_int_translated'	=>	(is=>'rw', clearer => 'clear_array_int_translated');
has 	'array_mz_translated'	=>	(is=>'rw', clearer => 'clear_array_mz_translated');

##### Precursor information
has 	'precursor_mz'			=>	(is=>'rw',isa=>'Num');
has 	'precursor_z'			=>	(is=>'rw',isa=>'Int');
has 	'precursor_int'			=>	(is=>'rw',isa=>'Num');
has		'fragmentation'			=>	(is=>'rw',isa=>'fragmentation_type',trigger =>\&_load_fragmentation,);
has 	'fragmentation_cv'		=>	(is=>'rw',isa=>'Str');

sub _load_fragmentation{
	my ($self)=shift;
	my %fragmentations=%{$FRAGMENTATIONS};
	$self->fragmentation_cv($fragmentations{$self->fragmentation});
	
}


sub translate_spectra{
	my ($self)=shift;
	
	my $mz_endianess;
	my $int_endianess;
	my $mz_prec;
	my $int_prec;
	
	my $mz_decoded;
	my $int_decoded;
	
	####	Endianess (litlle/big)
	if($self->mz_ArrayBinary_endian eq 'big'){
		$mz_endianess='>';
	}
	elsif($self->mz_ArrayBinary_endian eq 'little'){
		$mz_endianess='<';
	}
	if($self->int_ArrayBinary_endian eq 'big'){
		$int_endianess='>';
	}
	elsif($self->int_ArrayBinary_endian eq 'little'){
		$int_endianess='<';
	}
	return unless $self->mz_ArrayBinary_array;
	return unless $self->int_ArrayBinary_array;
	$mz_decoded  = decode_base64($self->mz_ArrayBinary_array);
	$int_decoded = decode_base64($self->int_ArrayBinary_array);
	#print $self->mz_ArrayBinary_array;
	#print Dumper $mz_decoded;
	
	$mz_decoded=uncompress_zlib($mz_decoded)   if $self->mz_ArrayBinary_compression eq 'zlib_compression';
	$int_decoded=uncompress_zlib($int_decoded) if $self->int_ArrayBinary_compression eq 'zlib_compression';
	
	my (@mzs,@ints);
	
	####    Decoding 32/64
	if($self->mz_ArrayBinary_precission eq '32'){
		$mz_prec='f';
	}
	elsif($self->mz_ArrayBinary_precission eq '64'){
		$mz_prec='d';
	}
	if($self->int_ArrayBinary_precission eq '32'){
		$int_prec='f';
	}
	elsif($self->int_ArrayBinary_precission eq '64'){
		$int_prec='d';
	}
	
	@mzs = unpack("$mz_prec$mz_endianess*", $mz_decoded);
	#print Dumper @mzs;
	@ints = unpack("$int_prec$int_endianess*", $int_decoded);
	$self->	array_int_translated (\@ints);
	$self->	array_mz_translated	 (\@mzs);
}


sub uncompress_zlib{
	#http://perldoc.perl.org/Compress/Zlib.html
	my $data=shift;
	my ($output, $status);
	my $x = inflateInit() or die "Cannot create a inflation stream\n" ;
	($output, $status)=$x->inflate(\$data) ;
	return undef unless $status == Z_STREAM_END;
	return $output;	
}

sub compress_zlib{
	my $data=shift;
	my ($output, $status);
	my $x = deflateInit() or die "Cannot create a inflation stream\n" ;
	($output, $status)=$x->deflate($data) ;
	($output, $status) = $x->flush() ;
	return undef unless $status == Z_OK;
	return $output;	

}















__PACKAGE__->meta->make_immutable;

